Skip to content

fix(core): honor the gitignore \# escape when loading ignore patterns - #1540

Closed
wangzhengzhuo05 wants to merge 1 commit into
basicmachines-co:mainfrom
wangzhengzhuo05:fix/bmignore-hash-escape
Closed

wangzhengzhuo05 wants to merge 1 commit into
basicmachines-co:mainfrom
wangzhengzhuo05:fix/bmignore-hash-escape

Conversation

@wangzhengzhuo05

Copy link
Copy Markdown
Contributor

What

.bmignore (and project .gitignore) could not express any pattern beginning with #.

Why

Both loaders used a naive line filter that skipped every line starting with # and never
unescaped a leading backslash:

  • #*# was silently dropped as a comment.
  • \#*# was stored literally, and should_ignore_path() matches with fnmatch, which
    has no escape syntax — so it matched nothing.

The failure mode is silent and misleading: the pattern looks right in .bmignore and
simply never loads. It makes Emacs autosave files (#file#) impossible to ignore through
the documented gitignore-style syntax, even though the shipped defaults already cover
*~, *.swp and *.swo. As the issue notes, the matcher itself handles #*# correctly
(it fnmatches each path part) — only the loader was broken.

How

Added one small module-level helper, _parse_ignore_pattern_line(), and routed both the
.bmignore and .gitignore read loops through it:

  • blank lines and comments (leading #) are still skipped;
  • a line starting with \# now yields the pattern with one leading backslash stripped,
    per the gitignore rule "Put a backslash in front of the first hash for patterns that
    begin with a hash"
    — so \#*# loads as #*# and matches.

should_ignore_path() is unchanged, DEFAULT_IGNORE_PATTERNS is unchanged (adding a
default #*# would be a separate product decision), and no dependency was added
(no pathspec).

Tests

Three regression tests in tests/cli/test_ignore_utils.py:

  • test_bmignore_escaped_hash_pattern_loads\#*# in .bmignore loads as #*#, the
    comment line is still skipped, and should_ignore_path("notes/#note.md#") is True.
  • test_bmignore_bare_hash_is_still_a_comment — a bare #*# remains a comment.
  • test_gitignore_escaped_hash_pattern_loads — same escape handling for a project
    .gitignore.
$ .venv/bin/python -m pytest tests/cli/test_ignore_utils.py -q
17 passed
$ ruff check  (both files)         -> All checks passed!
$ ruff format --check (both files) -> 2 files already formatted

Mutation check

Reverting only the escape handling in _parse_ignore_pattern_line() (the \# branch
removed, everything else intact) fails exactly the two tests that guard the fix:

FAILED tests/cli/test_ignore_utils.py::test_bmignore_escaped_hash_pattern_loads
FAILED tests/cli/test_ignore_utils.py::test_gitignore_escaped_hash_pattern_loads
2 failed, 1 passed, 14 deselected

Restoring the fix → 17 passed.

Fixes #1539

.bmignore documents gitignore-style syntax, but the loader skipped any line
beginning with '#' and never unescaped a leading backslash, so no pattern
beginning with a hash could take effect: '#*#' was dropped as a comment and
'\#*#' was stored literally and never matched. Strip one leading backslash when
a line starts with '\#', for both .bmignore and project .gitignore, so e.g.
'\#*#' loads as '#*#' (Emacs autosave files).

Fixes basicmachines-co#1539

Signed-off-by: wangzhengzhuo05 <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fbffd1a117

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +78 to +82
line = raw_line.strip()
if not line or line.startswith("#"):
return None
if line.startswith("\\#"):
return line[1:]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve leading whitespace before unescaping hashes

When a .gitignore contains \#foo, a valid pattern for the filename #foo, strip() removes the significant leading space and the new escape branch converts the remainder to #foo. Basic Memory therefore ignores/index-excludes the wrong file, while git check-ignore --no-index matches only #foo; Git's pattern format discards unescaped trailing spaces, not leading ones. Preserve leading characters when removing the line ending before recognizing the hash escape.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in replacement #1544, commit ee2f854. The parser preserves leading whitespace, decodes escaped hashes/spaces, and discards only unescaped trailing spaces. Regressions cover the reported space-plus-escaped-hash case for both loaders: 39 focused tests pass, all 22 filename expectations agree with git check-ignore, and just fast-check plus just doctor pass. Exact-head Codex gate is currently waiting on #1544.

@phernandez

Copy link
Copy Markdown
Member

Thanks @wangzhengzhuo05 for the fix and regressions. The replacement is now open for review at #1544, based on current main. It preserves your original commit and author/DCO attribution (rebased without changing its patch), with a separate maintainer commit for the whitespace correction.

The Codex P2 is addressed in ee2f8545bc57f49ae3278bef815dc3a9b019cf9f: \#foo matches #foo and does not exclude #foo; unescaped trailing spaces are removed and escaped trailing spaces remain literal. Both loaders have focused regressions. All 39 ignore tests, just fast-check, and just doctor pass, and Git agrees with all 22 new positive/negative filename expectations.

Replacement #1544 is in exact-head CI/Codex review now (Codex gate: waiting). Nothing has been merged; this original PR remains open while the replacement completes review.

@wangzhengzhuo05

Copy link
Copy Markdown
Contributor Author

Superseded by #1544 (merged), which preserves this patch's commit and authorship. Closing to keep the review queue clean — thanks @phernandez for carrying it forward with the whitespace correction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] .bmignore cannot express any pattern beginning with #, despite documenting gitignore-style syntax

2 participants